<template>
    <div class="file-upload">
        <div class="file-upload-container" @click="triggerUploader" v-bind="$attrs">
            <slot v-if="fileStatus === 'ready'" name="default">
                <button class="btn btn-primary">点击上传</button>
            </slot>
            <slot v-else-if="fileStatus === 'loading'" name="loading">
                <button class="btn btn-primary" disabled>正在上传</button>
            </slot>
            <slot v-else-if="fileStatus === 'success'" name="uploaded" :uploadedData="uploadedData">
                <button class="btn btn-primary">上传成功</button>
            </slot>
        </div>
        <input type="file" class="file-input d-none" ref="fileInput" @change="handleFileChange" />
    </div>
</template>
<script lang="ts" setup>
import { PropType, ref } from "vue";
import axios from "@/services/axios";

const fileInput = ref<null | HTMLInputElement>(null)
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type CheckFunction = (file: File) => boolean

const props = defineProps({
    action: {
        type: String,
        required: true
    },
    beforeUpload: {
        type: Function as PropType<CheckFunction>
    }
})

const emits = defineEmits(['file-uploaded', 'file-uploaded-error'])

// 给slot使用。传递给使用组件的地方使用
const uploadedData = ref()

//上传状态
const fileStatus = ref<UploadStatus>('ready')

// 点击上传按钮事件
const triggerUploader = () => {
    if (fileInput.value) {
        fileInput.value.click()
    }
}

const handleFileChange = (e: Event) => {
    // 断言成HTMLInputElement ，要不然只能取到dom标签
    const target = e.target as HTMLInputElement
    // 断言成FileList 要不然使用时候报null错误
    const files = target.files as FileList
    if (files.length > 0) {
        const file = files[0]
        if (props.beforeUpload) {
            const result = props.beforeUpload(file)
            if (!result) {
                return
            }
        }
        fileStatus.value = 'loading'
        const formData = new FormData()
        formData.append(file.name, file)
        axios.post(props.action, formData).then((res: any) => {
            fileStatus.value = 'success'
            uploadedData.value = res.data
            emits('file-uploaded', res.data)
        }).catch((e: Error) => {
            fileStatus.value = 'error'
            emits('file-uploaded-error', e)
        }).finally(() => {
            if (fileInput.value) {
                // 不管成功失败，把dom节点上的value设置为空
                fileInput.value.value = ''
            }
        })
    }
}
</script>
<script lang="ts">
export default {
    // 引用组件的地方，添加class时，默认不绑定到外层元素。在组件里配合v-bind="$attrs"使用
    inheritAttrs: false
}
</script>
<style lang="scss">
</style>
